﻿using System;
// ReSharper disable RedundantUsingDirective
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Diagnostics;
using System.Linq;
using Gqq.CodeFirst.Palace;
using Gqq.Common.Helper;
using Gqq.Common.Helper.CodeFirstLog;
// ReSharper restore RedundantUsingDirective

namespace Gqq.CodeFirst.ProcessPalace
{
    public class Part6 : PartBase
    {
        public static void DoTest()
        {
            //OpenOrCloseLazyLoading();
//            TestEagerlyLoading();
            TestApplyFilters();
        }

        #region 测试Eagerly loading
#pragma warning disable 168
        /// <summary>
        /// Eager loading 是查询一个实体查询相关实体作为部分查询。
        /// </summary>
        private static void TestEagerlyLoading2()
        {
            //1、通过以下方法可以查询出所有的castle、lady_in_waiting和princess。
            var castles1 = db.Castles.Include(x => x.LadiesInWaiting.Select(y => y.Princess)).ToList();

            var princess1 = db.Princesses.Where(p => p.Name == "Cinderella").Include(x => x.Unicorns).FirstOrDefault();
            //2、Load all unicorns, all related princesses, and all related ladies
            var unicorns1 = db.Unicorns
                                .Include(u => u.Princess.LadiesInWaiting)
                                .ToList();
            // 3、Load all castles, all related ladies, and all related princesses
            // using a string to specify the relationships
            var castles2 = db.Castles
                               .Include("LadiesInWaiting.Princess")
                               .ToList();

            // 4、Load all unicorns, all related princesses, and all related ladies
            // using a string to specify the relationships
            var unicorns2 = db.Unicorns
                                .Include("Princess.LadiesInWaiting")
                                .ToList();

        }

        /// <summary>
        /// Eager loading is the process whereby a query for one type of entity also loads related entities as part of the query.
        ///  Eager loading is achieved by use of the Include method. 
        /// For example, the queries below will load princesses and all the unicorns related to each princess.
        /// 
        /// Eager loading是指在查询一个实体的过程中，将指定与该对象相关属性的查询。
        /// 以下的例子会将pricess对象和与它相关的unicorns完全查出来。
        /// </summary>
        public static void TestEagerlyLoading()
        {
            var context = db;

            // Load all princesses and related unicorns
            var princesses1 = context.Princesses
                                  .Include(p => p.Unicorns)
                                  .ToList();

            // Load one princess and her related unicorns
            var princess1 = context.Princesses
                                .Where(p => p.Name == "Cinderella")
                                .Include(p => p.Unicorns)
                                .FirstOrDefault();

            //如果lazy loading是打开的，在debug页面看不到这个值，但是在输出的时候，执行一次读取数据库的操作。
            Console.WriteLine(princess1.Unicorns.FirstOrDefault().Name);

            // Load all princesses and related unicorns using a string
            // to specify the relationship
            var princesses2 = context.Princesses
                                  .Include("Unicorns")
                                  .ToList();

            // Load one princess and her related unicorns using a string
            // to specify the relationship
            var princess2 = context.Princesses
                                .Where(p => p.Name == "Cinderella")
                                .Include("Unicorns")
                                .FirstOrDefault();
        }
#pragma warning restore 168
        #endregion

        #region 关闭了Lazyloading，使用load方法显式加载。
        /// <summary>
        /// 就算lazy loading关闭了，也可以通过load方法显式的进行调用
        /// </summary>
        public static void TestLoad()
        {
            var context = db;
            var unicorn = context.Unicorns.Find(5);
            var princess = context.Princesses.Find(21);

            // Load the princess related to a given unicorn 
            context.Entry(unicorn).Reference(u => u.Princess).Load();

            // Load the princess related to a given unicorn using a string 
            context.Entry(unicorn).Reference("Princess").Load();

            // Load the unicorns related to a given princess 
            context.Entry(princess).Collection(p => p.Unicorns).Load();

            // Load the unicorns related to a given princess using a string to 
            // specify the relationship 
            context.Entry(princess).Collection("Unicorns").Load();
        } 
        #endregion

        #region 实用Query方法计算相关实体个数，但是不从数据库读取它们。
        //Using Query to count related entities without loading them

        //Sometimes it is useful to know how many entities are related to another entity 
        //in the database without actually incurring the cost of loading all those 
        //entities. The Query method with the LINQ Count method can be used to do this. 

        /// <summary>
        /// 有时候，我们需要判断一个类中有多少个实体，但是并不需要真正的查询出这几个实体。
        /// 这时候Query方法和LINQ Count方法就很实用。举例如下：
        /// </summary>
        public static void TestRelatedEntityCount()
        {
            var princess = db.Princesses.Find(19);

            var unicornHaul = db.Entry(princess).Collection(x => x.Unicorns).Query().Count();
            Console.WriteLine("Princess includes {0} Unicorns", unicornHaul);

            var princessList = db.Princesses.ToList();
            foreach (Princess pr in princessList)
            {
                var count = db.Entry(pr).Collection(x => x.Unicorns).Query().Count();
                Console.WriteLine("Princess {0} includes {1} Unicorns", pr.Name, count);
            }
        }


        #endregion

        #region 对明显的加载关联属性提供过滤器
        //Applying filters when explicitly loading related entities

        //The Query method provides access to the underlying query that the Entity 
        //Framework will use when loading related entities. You can then use LINQ to 
        //apply filters to the query before executing it with a call to a LINQ 
        //extensionmethod such as ToList, Load, etc. The Query method can be used with 
        //both reference and collection navigation properties but is most useful for 
        //collections where it can be used to load only part of the collection. 

        /// <summary>
        /// 当EF用Query方法来加载关联实体的时候，其实是对于实体做了潜在的查询。在读取数据库之前，
        /// 你可以加一些过滤条件。（使用ToList、Load这些方法来执行数据库操作）。Query方法既可以
        /// 用在引用、也可以用在导航属性上。它主要用在类型为Collection的导航属性上，这样就可以
        /// 取到部分Collection
        /// 
        /// </summary>
        public static void TestApplyFilters()
        {
            var princess = db.Princesses.Find(19);

            //检查是否被加载。
            Console.WriteLine(db.Entry(princess).Collection(x => x.Unicorns).IsLoaded);

            // 手动加载princess中的Unicorns
            //如果没有query方法，压根就不能加条件，不存在Entry(princess).Collection(x => x.Unicorns).Where...
            db.Entry(princess).Collection(x => x.Unicorns).
                Query().
                Where(x => x.Name.StartsWith("B")).
                Load();

            //Query方法调用之后是非泛型的IQueryable，所以要使用Cast方法。
            db.Entry(princess).Collection("Unicorns").
                Query().
                Cast<Unicorn>().
                Where(x => x.Name.StartsWith("B")).
                Load();

            Console.WriteLine(db.Entry(princess).Collection(x => x.Unicorns).IsLoaded);
            //            
            //When using the Query method it is usually best to turn off lazy loading for the 
            //navigation property. This is because otherwise the entire collection may get 
            //loaded automatically by the lazy loading mechanism either before or after the 
            //filtered query has been executed.
            //如果使用Query，最好关闭lazy loading，因为由于这个lazy loading机制，整个Collection会自动加载，
            //有可能在filter执行之前，也有可能在filter执行之后。

        }


        #endregion

        #region 关闭非延时加载。

        /// <summary>
        /// 要关闭LazyLoading，有两种方法。
        /// 1、重写OnModelCreating方法，Configuration.LazyLoadingEnabled = false;
        /// 2、可以把Princess中的Unicorns属性改为非virtual
        /// 如果关闭了LazyLoading，要加载navigate properties，需要使用load方法。
        /// </summary>

        public static void OpenOrCloseLazyLoading()
        {
            var ps = db.Princesses.Find(19);
            Console.WriteLine(ps.Name);

            //如果是非延时加载，必须要用Load方法手动加载。
            // Load the unicorns related to a given princess 
            db.Entry(ps).Collection(x => x.Unicorns).Load();
            db.Entry(ps).Collection("Unicorns").Load();
            foreach (var u in ps.Unicorns)
            {
                Console.WriteLine(u.Name);
            }

            //使用Load方法加载Princess
            //如果lazy loading=false。则princess不会加载。必须要用load手动加载。
            var unicorn = db.Unicorns.Find(7);
            var isLoaded = db.Entry(unicorn).Reference(x => x.Princess).IsLoaded;
            Console.WriteLine("first isLoaded:{0}", isLoaded);

            db.Entry(unicorn).Reference(x => x.Princess).Load();
            isLoaded = db.Entry(unicorn).Reference(x => x.Princess).IsLoaded;
            Console.WriteLine("end isLoaded:{0}", isLoaded);

            Console.WriteLine(unicorn.Princess.Id);
        }
        #endregion
    }
}